﻿/// <reference path="../Scripts/jquery.js" />
/// <reference path="d3.js" />

function Chart(_srcElm, _opt) {
    var instance = this;
    var option = $.extend(ChartOption, _opt);
    var drawer = d3.select(_srcElm).append("svg").attr("class", option.BkStyle);
    var charts = {};
    var data = [];
    var padding = [30, 30, 30, 30];
    var title = null;
    var theme = new ChartTheme();
    instance.rootElm = $(_srcElm).css("position", "relative");

    instance.Title = function (_titleElm) {
        /// <summary>
        /// 获取或设置标题元素
        /// </summary>
        /// <param name="_titleElm" type="JQuery Object or DOM">标题元素是一个标准DOM元素</param>
        /// <returns type=""></returns>
        if (_titleElm) {
            title = _titleElm;
            return instance;
        }
        else {
            return title;
        }
    };
    instance.Padding = function (_padding) {
        /// <summary>
        /// 获取或设置四边留白（可绘制坐标轴等内容）
        /// </summary>
        /// <param name="_padding">Array[top,right,bottom,left]</param>
        /// <returns type=""></returns>
        if (!_padding || !_padding.length || _padding.length < 4) {
            return padding;
        }
        else {
            padding = _padding;
            return instance;
        }
    };
    instance.Theme = function () {
        return theme;
    };
    instance.Item = function (_name, _chart) {
        /// <summary>
        /// 获取或设置图表中的绘图系列
        /// </summary>
        /// <param name="_name"></param>
        /// <param name="_chart"></param>
        /// <returns type=""></returns>
        if (_chart) {
            if (charts[_name]) {
                charts[_name].Dispose();
                charts[_name] = _chart;
            }
            else {
                charts[_name] = _chart;
            }
        }
        else if (_name) {
            return charts[_name];
        }
        else {
            return charts;
        }
    };
    instance.Data = function (_data) {
        /// <summary>
        /// 获取或设置图表的数据源
        /// </summary>
        /// <param name="_data"></param>
        /// <returns type=""></returns>
        if (_data) {
            data = _data;
            return instance;
        }
        else {
            return data;
        }
    };
    instance.Render = function () {
        /// <summary>
        /// 重新绘制所有图表
        /// </summary>
        /// <returns type=""></returns>

        //处理标题
        if (title) {
            instance.rootElm.append(title);
        }

        //准备画布
        drawer.attr("width", instance.rootElm.innerWidth() - padding[1] - padding[3])
              .attr("height", instance.rootElm.innerHeight() - padding[0] - padding[2]);

        //处理色彩主题
        theme.Prepare(data.length);

        for (var i in charts) {
            if (charts[i].Draw) {
                charts[i].Draw(drawer, data, instance);
            }
        }
        return this;
    };
    instance.LastEvent = function () {
        /// <summary>
        /// 获取最后触发的事件
        /// </summary>
        /// <returns type=""></returns>
        return d3.event;
    };

    return this;
}
ChartOption = {
    BkStyle: ""  //整个画布的样式类
}

function ChartAxis() {
    var instance = this;

    this.Range = function (_min, _max) {
        /// <summary>
        /// 设置X或Y轴的值范围
        /// </summary>
        /// <param name="_min"></param>
        /// <param name="_max"></param>
        /// <returns type=""></returns>
        instance.Min = _min;
        instance.Max = _max;
    };

    instance.Unit = 1;
    instance.Min = 0;
    instance.Max = 1;
    return instance;
}

function ChartTheme() {
    var instance = this;
    var themeColors = [];
    var router = null;  //用于将值映射为对应的颜色
    var flader = null;  //用于将值映射为颜色的淡化量
    var groupCount = null; //每个颜色需用于多少个色块

    this.AddColor = function (r, g, b) {
        /// <summary>
        /// 向当前主题对象中添加一种颜色
        /// </summary>
        /// <returns type=""></returns>
        if (arguments.length == 0) {
            return instance;
        }
        else if (arguments.length == 1) {
            themeColors.push(d3.rgb(r));
        }
        else {
            themeColors.push(d3.rgb(r, g, b));
        }
        return instance;
    };

    this.Prepare = function (_num) {
        /// <summary>
        /// 设置要提供的插值色彩数量
        /// </summary>
        /// <param name="_num">需要的颜色数量</param>
        /// <returns type=""></returns>
        if (themeColors.length == 0) {
            //如果没设置主题色，提供默认的三种颜色作为主题
            instance.AddColor(241, 97, 97).AddColor(13, 85, 123).AddColor(56, 181, 185).AddColor(252, 185, 70);
        }
        //将0～1间的插值系数映射到不同的色彩上
        var r = [];
        for (var i = 0; i < themeColors.length; i++) {
            r.push(i);
        }
        groupCount = Math.ceil(_num / themeColors.length);
        router = d3.scale.quantile().domain([0, 1]).range(r); //按绘制目标项的逻辑位置映射到色块
        flader = d3.scale.linear().domain([0, groupCount]).range([0, 1.2]);  //再按淡化比例计算色彩的淡化值,最大1.5,到2就成白色了
    };

    this.GetLinearColor = function (_pos, _index) {
        /// <summary>
        /// 获取一个根据主题色彩线性插值计算得到的色彩
        /// </summary>
        /// <param name="_pos">插值参数，0~1间的小数</param>
        /// <returns type=""></returns>
        var i = router(_pos);
        return themeColors[i].brighter(flader(_index % groupCount));
    };

    this.GetColor = function (_pos) {
        var i = router(_pos);
        return themeColors[i];
    }

    return instance;
}

function RowChart(_opt) {
    /// <summary>
    /// 以横条样式表现数据的图表
    /// </summary>
    /// <param name="_opt"></param>
    var instance = this;
    var option = $.extend({}, RowChartOption, _opt);
    var dwr = null;
    var rows = [];
    var xAxis = new ChartAxis();
    var yAxis = new ChartAxis();
    var drawXAxis = false;
    var drawYAxis = false;
    var txtHeight = 30;

    this.af_getValue = function (d, j) {
        /// <summary>
        /// 虚方法，用于获取条块的值
        /// </summary>
        /// <param name="d">准备绘制的数据对象</param>
        /// <param name="j">当前绘制的数据项索引</param>
        /// <returns type="Int">宽度值</returns>
    };

    this.af_getLable = function (d, j) {
        /// <summary>
        /// 获取数据项对应的标签
        /// </summary>
        /// <param name="d"></param>
        /// <param name="j"></param>
        /// <returns type=""></returns>
    };

    this.vf_getValuePos = function (d, j, length) {
        /// <summary>
        /// 可覆盖此函数，用于计算当前要绘制的对象的逻辑位置，用于决定用色彩表示的值排名。默认为当前绘制项的索引位置
        /// </summary>
        /// <param name="d">当前要绘制的对象</param>
        /// <param name="j">当前是绘制第几个</param>
        /// <param name="length">最大数据项</param>
        /// <returns type="float">以小数表示的数据项的排名</returns>
        return j / length;
    };

    this.XAxis = function () {
        return xAxis;
    };

    this.YAxis = function () {
        return yAxis;
    };

    this.EnableXAxis = function (_switch) {
        /// <summary>
        /// 开启X轴绘制【如开启，则当前图表中其他数据系列将不再绘制X轴】
        /// </summary>
        /// <returns type=""></returns>
        drawXAxis = _switch ? true : false;
        return instance;
    };

    this.EnableYAxis = function (_switch) {
        /// <summary>
        /// 开启X轴绘制【如开启，则当前图表中其他数据系列将不再绘制Y轴】
        /// </summary>
        /// <returns type=""></returns>
        drawYAxis = _switch ? true : false;
        return instance;
    };

    this.Draw = function (drawer, data, chartHost) {
        /// <summary>
        /// 绘制图表
        /// </summary>
        /// <param name="drawer">绘制器，本质是一个d3对象</param>
        /// <param name="data">要绘制的数据系列</param>
        /// <param name="xAxis">横轴对象</param>
        /// <param name="yAxis">纵轴对象</param>
        /// <param name="chartHost">表示当前整个图表的实例对象</param>
        /// <returns type=""></returns>
        if (!dwr) {
            dwr = drawer;
            //测试文本高度
            var tmpTxt = dwr.append("text").text("测试").attr("fill", "transpercent").attr("class", "tmpText");
            txtHeight = tmpTxt[0][0].clientHeight;
            $(".tmpText").remove();
        }
        var tw = drawer.attr("width") - chartHost.Padding()[1] - chartHost.Padding()[3]; //绘制区域的宽度
        var th = drawer.attr("height") - chartHost.Padding()[0] - chartHost.Padding()[2]; //绘制区域的高度

        xAxis.Unit = tw / xAxis.Max;
        //yAxis.Unit = th / yAxis.Max;
        var ln = data.length;  //总数据项数
        var h = (th - (data.length - 1) * option.RowPadding) / data.length;  //每个条块的高度=画布高度/条块数

        //清除前次绘制的内容
        $(dwr[0]).html("");
        var rects = dwr.selectAll("rect")
              .data(data)
              .enter()
              .append("rect")
              .attr("height", option.RowHeight)
              .attr("y", function (d, j) { return (j + 1) * option.RowPadding + j * h + (h - option.RowHeight) / 2; })
              .attr("x", chartHost.Padding()[3])
              .attr("width", 0)
              .transition()
              .duration(1000)
              .ease("out")
              .attr("width", function (d, j) { return instance.af_getValue(d, j) * xAxis.Unit; })
              .attr("fill", function (d, j) { return option.EnableLineColor === true ? chartHost.Theme().GetLinearColor(instance.vf_getValuePos(d, j, ln), j) : chartHost.Theme().GetColor(instance.vf_getValuePos(d, j, ln)); });
        var txts = dwr.selectAll("text")
            .data(data)
            .enter()
            .append("text")
            .text(instance.af_getLable)
            .attr("fill", "black")
            .attr("y", function (d, j) { return (j + 2) * option.RowPadding + j * h + (h - option.RowHeight) / 2 + txtHeight / 2; })
            .attr("x", chartHost.Padding()[3] + 10);

        if (drawXAxis == true) {
            var xScaler = d3.scale.linear().domain([0, xAxis.Max]).range([0, tw]);
            var xLing = d3.svg.axis().scale(xScaler).orient("bottom").tickSize(4, 4).ticks(5);
            var x = dwr.append("g").call(xLing);
            x.attr("transform", new Transform().Translate(chartHost.Padding()[1], th + x[0][0].getBBox().height).toString())
        }
        for (var i = 0; i < rects[0].length; i++) {
            var r = {};
            r.rect = rects[0][i];
            r.text = txts[0][i];
            if (option.DrawAdorment) {
                var a = instance.vf_getRowAdorment(data[i], i);
                if (a) {
                    a.css("position", "absolute");
                    a.css("left", instance.af_getValue(data[i], i) * xAxis.Unit + chartHost.Padding()[3] + 5);
                    a.css("top", parseFloat($(r.rect).attr("y")));
                    r.adorment = a;
                    $(dwr[0]).parent().append(a);
                }
            }
            rows.push(r);
        }
        return [drawXAxis, drawYAxis];
    };

    this.vf_getRowAdorment = function (d, j) {
        /// <summary>
        /// 可覆盖此函数并为每个绘制的条块增加附属元素
        /// </summary>
        /// <param name="d"></param>
        /// <param name="j"></param>
        /// <returns type="">DOM OR JQuery Object</returns>
        return null;
    };

    this.Dispose = function () {
        dwr = null;
        return this;
    };

    return instance;
}
RowChartOption = {
    RowPadding: 10,
    RowHeight: 40,   //条块绘制高度，默认是固定30高度，如设为Null，则按图表区域平分
    EnableLineColor: true,
    DrawAdorment: false  //在每个条块的旁边添加附属元素
}

function RowStackChart(_opt) {
    /// <summary>
    /// 以堆叠横条样式表现含子项的数据的图表
    /// </summary>
    /// <param name="_opt"></param>
    var instance = this;
    var option = $.extend({}, RowStackChartOption, _opt);
    var dwr = null;
    var rows = [];
    var xAxis = new ChartAxis();
    var yAxis = new ChartAxis();
    var drawXAxis = false;
    var drawYAxis = false;
    var txtHeight = 30;

    this.af_getValue = function (d, j) {
        /// <summary>
        /// 虚方法，用于获取条块的值
        /// </summary>
        /// <param name="d">准备绘制的数据对象</param>
        /// <param name="j">当前绘制的数据项索引</param>
        /// <returns type="Int">宽度值</returns>
    };

    this.af_getLable = function (d, j) {
        /// <summary>
        /// 获取数据项对应的标签
        /// </summary>
        /// <param name="d"></param>
        /// <param name="j"></param>
        /// <returns type=""></returns>
    };

    this.vf_getValuePos = function (d, j, length) {
        /// <summary>
        /// 可覆盖此函数，用于计算当前要绘制的对象的逻辑位置，用于决定用色彩表示的值排名。默认为当前绘制项的索引位置
        /// </summary>
        /// <param name="d">当前要绘制的对象</param>
        /// <param name="j">当前是绘制第几个</param>
        /// <param name="length">最大数据项</param>
        /// <returns type="float">以小数表示的数据项的排名</returns>
        return j / length;
    };

    this.XAxis = function () {
        return xAxis;
    };

    this.YAxis = function () {
        return yAxis;
    };

    this.EnableXAxis = function (_switch) {
        /// <summary>
        /// 开启X轴绘制【如开启，则当前图表中其他数据系列将不再绘制X轴】
        /// </summary>
        /// <returns type=""></returns>
        drawXAxis = _switch ? true : false;
        return instance;
    };

    this.EnableYAxis = function (_switch) {
        /// <summary>
        /// 开启X轴绘制【如开启，则当前图表中其他数据系列将不再绘制Y轴】
        /// </summary>
        /// <returns type=""></returns>
        drawYAxis = _switch ? true : false;
        return instance;
    };

    this.Draw = function (drawer, data, chartHost) {
        /// <summary>
        /// 绘制图表
        /// </summary>
        /// <param name="drawer">绘制器，本质是一个d3对象</param>
        /// <param name="data">要绘制的数据系列</param>
        /// <param name="xAxis">横轴对象</param>
        /// <param name="yAxis">纵轴对象</param>
        /// <param name="chartHost">表示当前整个图表的实例对象</param>
        /// <returns type=""></returns>
        if (!dwr) {
            dwr = drawer;
            //测试文本高度
            var tmpTxt = dwr.append("text").text("测试").attr("fill", "transpercent").attr("class", "tmpText");
            txtHeight = tmpTxt[0][0].clientHeight;
            $(".tmpText").remove();
        }
        dwr.selectAll("g").remove();
        rows = [];
        var tw = drawer.attr("width") - chartHost.Padding()[1] - chartHost.Padding()[3]; //绘制区域的宽度
        var th = drawer.attr("height") - chartHost.Padding()[0] - chartHost.Padding()[2]; //绘制区域的高度

        xAxis.Unit = tw / xAxis.Max;
        //yAxis.Unit = th / yAxis.Max;
        var ln = data.length;  //总数据项数
        var h = (th - (data.length - 1) * option.RowPadding) / data.length;//每个条块的高度=画布高度/条块数

        var gs = dwr.selectAll("g")
              .data(data)
              .enter()
              .append("g");

        var rects1 = gs.append("rect")
              .attr("y", function (d, j) { return (j + 1) * option.RowPadding + j * h + (h - option.RowHeight) / 2; })
              .attr("x", chartHost.Padding()[3])
              .attr("height", option.RowHeight)  //TODO:条块绘制区域的高度也不一定与条块区域相等
              .attr("width", 0)
              .transition()
              .duration(1000)
              .ease("out")
              .attr("width", function (d, j) { return instance.af_getValue(d, j)[0] * xAxis.Unit; })
              .attr("fill", function (d, j) { return chartHost.Theme().GetLinearColor(instance.vf_getValuePos(d, j, ln), j); });

        var rects2 = gs.append("rect")
              .attr("y", function (d, j) { return (j + 1) * option.RowPadding + j * h + (h - option.RowHeight) / 2; })
              .attr("x", chartHost.Padding()[3])
              .attr("height", option.RowHeight)  //TODO:条块绘制区域的高度也不一定与条块区域相等
              .attr("width", 0)
              .transition()
              .duration(1000)
              .ease("out")
              .attr("width", function (d, j) { return instance.af_getValue(d, j)[1] * xAxis.Unit; })
              .attr("fill", function (d, j) { return chartHost.Theme().GetLinearColor(1 - instance.vf_getValuePos(d, j, ln), j); });

        var txts = gs.append("text")
            .text(instance.af_getLable)
            .attr("fill", "black")
            .attr("y", function (d, j) { return (j + 1) * option.RowPadding + j * h + (h - option.RowHeight) / 2 + txtHeight / 2; })
            .attr("x", chartHost.Padding()[3] + 10);

        if (drawXAxis == true) {
            var xScaler = d3.scale.linear().domain([0, xAxis.Max]).range([0, tw]);
            var xLing = d3.svg.axis().scale(xScaler).orient("bottom").tickSize(4, 4).ticks(5);
            var x = dwr.append("g").call(xLing);
            x.attr("transform", new Transform().Translate(chartHost.Padding()[1], th + x[0][0].getBBox().height).toString())
        }

        for (var i = 0; i < gs[0].length; i++) {
            var r = {};
            r.row = gs[0][i];
            r.text = txts[0][i];
            rows.push(r);
        }
        return [drawXAxis, drawYAxis];
    };

    this.Dispose = function () {
        dwr = null;
        return this;
    };

    return instance;
}
RowStackChartOption = {
    RowPadding: 10,
    RowHeight: 40,   //条块绘制高度，默认是将整个绘制区域按绘制的条块数量均分
    EnableLineColor: true
}

function ColumnChart(_opt) {
    /// <summary>
    /// 以纵样式表现数据的图表
    /// </summary>
    /// <param name="_opt"></param>
    var instance = this;
    var option = $.extend({}, ColumnChartOption, _opt);
    var dwr = null;
    var clmns = [];
    var xAxis = new ChartAxis();
    var yAxis = new ChartAxis();
    var drawXAxis = false;
    var drawYAxis = false;
    var txtHeight = 30;

    this.af_getValue = function (d, j) {
        /// <summary>
        /// 虚方法，用于获取条块的值
        /// </summary>
        /// <param name="d">准备绘制的数据对象</param>
        /// <param name="j">当前绘制的数据项索引</param>
        /// <returns type="Int">高度值</returns>
    };

    this.af_getLable = function (d, j) {
        /// <summary>
        /// 获取数据项对应的标签
        /// </summary>
        /// <param name="d"></param>
        /// <param name="j"></param>
        /// <returns type=""></returns>
    };

    this.vf_getValuePos = function (d, j, length) {
        /// <summary>
        /// 可覆盖此函数，用于计算当前要绘制的对象的逻辑位置，用于决定用色彩表示的值排名。默认为当前绘制项的索引位置
        /// </summary>
        /// <param name="d">当前要绘制的对象</param>
        /// <param name="j">当前是绘制第几个</param>
        /// <param name="length">最大数据项</param>
        /// <returns type="float">以小数表示的数据项的排名</returns>
        return j / length;
    };

    this.XAxis = function () {
        return xAxis;
    };

    this.YAxis = function () {
        return yAxis;
    };

    this.EnableXAxis = function (_switch) {
        /// <summary>
        /// 开启X轴绘制【如开启，则当前图表中其他数据系列将不再绘制X轴】
        /// </summary>
        /// <returns type=""></returns>
        drawXAxis = _switch ? true : false;
        return instance;
    };

    this.EnableYAxis = function (_switch) {
        /// <summary>
        /// 开启X轴绘制【如开启，则当前图表中其他数据系列将不再绘制Y轴】
        /// </summary>
        /// <returns type=""></returns>
        drawYAxis = _switch ? true : false;
        return instance;
    };

    this.Draw = function (drawer, data, chartHost) {
        /// <summary>
        /// 绘制图表
        /// </summary>
        /// <param name="drawer">绘制器，本质是一个d3对象</param>
        /// <param name="data">要绘制的数据系列</param>
        /// <param name="xAxis">横轴对象</param>
        /// <param name="yAxis">纵轴对象</param>
        /// <param name="chartHost">表示当前整个图表的实例对象</param>
        /// <returns type=""></returns>
        if (!dwr) {
            dwr = drawer;
            //测试文本高度
            var tmpTxt = dwr.append("text").text("测试").attr("fill", "transpercent").attr("class", "tmpText");
            txtHeight = tmpTxt[0][0].clientHeight;
            $(".tmpText").remove();
        }
        var tw = drawer.attr("width") - chartHost.Padding()[1] - chartHost.Padding()[3]; //绘制区域的宽度
        var th = drawer.attr("height") - chartHost.Padding()[0] - chartHost.Padding()[2]; //绘制区域的高度

        //xAxis.Unit = tw / xAxis.Max;
        yAxis.Unit = th / yAxis.Max;
        var ln = data.length;  //总数据项数
        var w = (tw - (data.length - 1) * option.ColumnPadding) / data.length;  //每个条块的宽度=画布宽度/条块数
        dwr.selectAll("rect").remove();
        dwr.selectAll("text").remove();
        var rects = dwr.selectAll("rect")
              .data(data)
              .enter()
              .append("rect")
              .attr("width", option.ColumnWidth)
              .attr("x", function (d, j) { return (j) * option.ColumnPadding + j * w + (w - option.ColumnWidth) / 2; })
              .attr("y", th)
              //.attr("height", 0)
              .transition()
              .duration(1000)
              .ease("out")
              .attr("y", function (d, j) { return th - instance.af_getValue(d, j) * yAxis.Unit + chartHost.Padding()[0]; })
              .attr("height", function (d, j) { return instance.af_getValue(d, j) * yAxis.Unit; })
              .attr("fill", function (d, j) { return chartHost.Theme().GetLinearColor(instance.vf_getValuePos(d, j, ln), j); });
        var txts = dwr.selectAll("text")
            .data(data)
            .enter()
            .append("text")
            .text(instance.af_getLable)
            .attr("fill", "black")
            .attr("y", th + txtHeight * 1.5)
            .attr("x", function (d, j) { return j * w + (w - this.clientWidth) / 2; });

        if (drawYAxis == true) {
            var yScaler = d3.scale.linear().domain([0, yAxis.Max]).range([th, 0]);
            var yLing = d3.svg.axis().scale(yScaler).orient("left").tickSize(4, 4).ticks(5);
            var y = dwr.append("g").call(yLing);
            y.attr("transform", new Transform().Translate(chartHost.Padding()[1] * 2, chartHost.Padding()[0]).toString())
        }

        for (var i = 0; i < rects[0].length; i++) {
            var r = {};
            r.rect = rects[0][i];
            r.text = txts[0][i];
            clmns.push(r);
        }
        return [drawXAxis, drawYAxis];
    };

    this.Dispose = function () {
        dwr = null;
        return this;
    };

    return instance;
}
ColumnChartOption = {
    ColumnPadding: 10,
    ColumnWidth: 40   //条块绘制宽度，默认是固定40宽，如设为Null，则按图表区域平分
}

function Transform() {
    var trans = "";
    this.Translate = function (x, y) {
        trans = "translate(" + x + "," + y + ")";
        return this;
    }

    this.toString = function () {
        return trans;
    }
    return this;
}

function TreemapChart(_opt) {
    /// <summary>
    /// 以不同尺寸的方块共同构成一个大矩形来表现数据的构成
    /// </summary>
    /// <param name="_opt"></param>
    var instance = this;
    var option = $.extend({}, TreemapChartOption, _opt);
    var dwr = null;

    this.af_getValue = function (d, j) {
        /// <summary>
        /// 虚方法，用于获取一段环的值
        /// </summary>
        /// <param name="d">准备绘制的数据对象</param>
        /// <param name="j">当前绘制的数据项索引</param>
        /// <returns type="Int">宽度值</returns>
    };

    this.af_getLable = function (d, j) {
        /// <summary>
        /// 获取数据项对应的标签
        /// </summary>
        /// <param name="d"></param>
        /// <param name="j"></param>
        /// <returns type=""></returns>
    };

    this.vf_getValuePos = function (d, j, length) {
        /// <summary>
        /// 可覆盖此函数，用于计算当前要绘制的对象的逻辑位置，用于决定用色彩表示的值排名。默认为当前绘制项的索引位置
        /// </summary>
        /// <param name="d">当前要绘制的对象</param>
        /// <param name="j">当前是绘制第几个</param>
        /// <param name="length">最大数据项</param>
        /// <returns type="float">以小数表示的数据项的排名</returns>
        return j / length;
    };

    this.Draw = function (drawer, data, chartHost) {
        /// <summary>
        /// 绘制图表
        /// </summary>
        /// <param name="drawer">绘制器，本质是一个d3对象</param>
        /// <param name="data">要绘制的数据系列</param>
        /// <returns type=""></returns>
        if (!dwr) {
            dwr = drawer;
        }
        var tw = drawer.attr("width"); //绘制区域的宽度
        var th = drawer.attr("height"); //绘制区域的高度

        var treemap = d3.layout.treemap()
                               .mode("squarify")
                               .size([tw, th])
                               .value(instance.af_getValue);

        var nodes = treemap.nodes(data);
        var links = treemap.links(nodes);
        var ln = nodes.length;

        var groups = dwr.selectAll("g")
                    .data(nodes.filter(function (d) { return !d.children; }))
                    .enter()
                    .append("g");

        var getPoxX;
        var getPosY;

        if (option.Sort == "DESC") {
            getPosX = function (d) {
                return tw - d.x - d.dx;
            };
            getPosY = function (d) {
                return th - d.y - d.dy;
            };
        }
        else {
            getPosX = function (d) {
                return d.x;
            };
            getPosY = function (d) {
                return d.y;
            };
        }

        var rects = groups.append("rect")
                          .attr("x", getPosX)
                          .attr("y", getPosY)
                          .transition()
                          .duration(1000)
                          .ease("in")
                          .attr("width", function (d) { return d.dx; })
                          .attr("height", function (d) { return d.dy; })
                          .style("fill", function (d, j) { return chartHost.Theme().GetLinearColor(instance.vf_getValuePos(d, j, ln), j); });

        var texts = groups.append("text")
                    .attr("x", getPosX)
                    .attr("y", getPosY)
                    .attr("dx", "0.5em")
                    .attr("dy", "1.5em")
                    .text(instance.af_getLable);
    };

    this.Dispose = function () {
        dwr = null;
        return this;
    };

    return instance;
};
TreemapChartOption = {
    Mode: "dice",  //方块的绘制模式，dice-只横条，slice-只竖条，squarify-混合
    Sort: "DESC",  //色块的排序，AES-升序，大的在最后，DESC-降序
};

function CircleChart(_opt) {
    /// <summary>
    /// 以环形的不同分段来表现数据的构成
    /// </summary>
    /// <param name="_opt"></param>
    var instance = this;
    var option = $.extend(CircleChartOption, _opt);
    var dwr = null;
    var items = [];
    var arc = null;
    var radius = [];
    var hotPieIndex = null;
    var sTime = null;
    var titleWraper = null;
    var txtHeight = null;
    var lgns = null; //图例

    function updateAni() {
        var du = new Date() - sTime;
        if (du > 1000) {
            //直接设置终值
            du = 1000;
        }
        for (var i = 0; i < items.length; i++) {
            //循环计算要增加的角度
            var aniRate = du / 1000; //动画执行进度
            aniRate = d3.ease("quad-out")(aniRate);
            arc.startAngle(items[i].__data__.startAngle * aniRate).endAngle(items[i].__data__.endAngle * aniRate);
            d3.select(items[i]).select("path").attr("d", arc(items[i].__data__.data));
        }
        if (du < 1000) {
            setTimeout(updateAni, 0);
        }
        else {
            sTime = null;
        }
    };

    function onLegendHover(ev) {
        var i = $(ev.currentTarget).attr("index");
        $(items[i]).eq(0).trigger("legendHover", { index: i });
    };

    function onLegendClick(ev) {
        var i = $(ev.currentTarget).attr("index");
        $(items[i]).eq(0).trigger("click", { index: i });
    };

    this.af_getValue = function (d, j) {
        /// <summary>
        /// 虚方法，用于获取一段环的值
        /// </summary>
        /// <param name="d">准备绘制的数据对象</param>
        /// <param name="j">当前绘制的数据项索引</param>
        /// <returns type="Int">宽度值</returns>
    };

    this.af_getLable = function (d, j) {
        /// <summary>
        /// 获取数据项对应的标签
        /// </summary>
        /// <param name="d"></param>
        /// <param name="j"></param>
        /// <returns type=""></returns>
    };

    this.vf_getValuePos = function (d, j, length) {
        /// <summary>
        /// 可覆盖此函数，用于计算当前要绘制的对象的逻辑位置，用于决定用色彩表示的值排名。默认为当前绘制项的索引位置
        /// </summary>
        /// <param name="d">当前要绘制的对象</param>
        /// <param name="j">当前是绘制第几个</param>
        /// <param name="length">最大数据项</param>
        /// <returns type="float">以小数表示的数据项的排名</returns>
        return j / length;
    };

    this.Draw = function (drawer, data, chartHost) {
        /// <summary>
        /// 绘制图表
        /// </summary>
        /// <param name="drawer">绘制器，本质是一个d3对象</param>
        /// <param name="data">要绘制的数据系列</param>
        /// <returns type=""></returns>
        if (!dwr) {
            dwr = drawer;
            var tmpTxt = dwr.append("text").text("测试").attr("fill", "transpercent").attr("class", "tmpText");
            txtHeight = tmpTxt[0][0].clientHeight;
            $(".tmpText").remove();
        }
        var tw = drawer.attr("width"); //绘制区域的宽度
        var th = drawer.attr("height"); //绘制区域的高度
        var w = Math.min(tw, th);   //按绘制区域的最小值确定环形的外切正方形
        w = w - option.Padding * 2;
        radius = [w / 2 - option.CircleWidth, w / 2];

        var dataset = [];
        for (var i = 0; i < data.length; i++) {
            dataset.push(instance.af_getValue(data[i]));
        }

        var ln = dataset.length;
        var pie = d3.layout.pie();
        arc = d3.svg.arc().innerRadius(radius[0]).outerRadius(radius[1]);
        dwr.selectAll("g").remove();
        if (lgns) {
            lgns.remove();
        }
        items = [];
        var arcs = dwr.selectAll("g")
                      .data(pie(dataset))
                      .enter()
                      .append("g")
                      .attr("index", function (d, j) { return j; })
                      .attr("label", instance.af_getLable)
                      .attr("transform", new Transform().Translate(radius[1] + option.Padding, radius[1] + option.Padding).toString());
        arcs.append("path")
            .attr("fill", function (d, j) { return chartHost.Theme().GetLinearColor(instance.vf_getValuePos(data[j], j, ln), j); })
            .attr("stroke", function (d, j) { return chartHost.Theme().GetLinearColor(instance.vf_getValuePos(data[j], j, ln), j); })
            .attr("stroke-width", "1");

        items = arcs[0];

        if (option.Legend === true) {
            //图例用标准DOM是不是更好？
            var h = $(dwr[0][0]).parent().eq(0);
            if (h.length > 0) {
                lgns = $("<ul>").appendTo(h).addClass(option.StyleForLegend);
                lgns.css("left", radius[1] * 2 + chartHost.Padding()[3] * 2)
                    .css("top", 0);
                for (var i = 0; i < items.length; i++) {
                    $("<li>").text($(items[i]).attr("label")).appendTo(lgns)
                             .attr("index", i)
                             .css("borderColor", $(items[i]).children("path").attr("fill"));
                }
                lgns.delegate("li", "mouseover", onLegendHover);
                lgns.delegate("li", "click", onLegendClick);
            }
        }

        sTime = new Date();

        setTimeout(updateAni, 0);
    };

    this.EnableLegend = function (_switch) {
        /// <summary>
        /// 启用或关闭图例
        /// </summary>
        /// <param name="_switch"></param>
        /// <returns type=""></returns>
        option.Legend = _switch;
        return instance;
    };

    this.Hot = function (i) {
        /// <summary>
        /// 突出显示环上的某一段
        /// </summary>
        /// <param name="i"></param>
        /// <returns type=""></returns>

        if (sTime) {
            //正处于初始动画状态，不能绘制
            return instance;
        }

        //原始属性
        var transX = radius[1] + option.Padding;
        var transY = radius[1] + option.Padding;

        if (!$.isNumeric(i)) {
            instance.Unhot();
            return instance;
        }
        if (items.length == 1) {
            //如果只有一项
            var s = items[i].__data__.startAngle;  //以弧度表示的起始角度
            var e = items[i].__data__.endAngle;
            var g = d3.select(items[i]);
            var clr = g.select("path").attr("fill");
            var d = arc.innerRadius(radius[0]).outerRadius(radius[0] + 3).startAngle(s).endAngle(e)(items[i].__data__.data);
            var nd = arc.innerRadius(radius[0] + 10).outerRadius(radius[1] + 10)(items[i].__data__.data);
            if ($.isNumeric(hotPieIndex) & hotPieIndex !== i) {
                instance.Unhot();
            }
            g.select("path").attr("fill", clr).attr("d", nd);
            g.append("path").attr("fill", clr).attr("d", d).attr("class", "hotPath");
            hotPieIndex = i;
        }
        else {
            var s = items[i].__data__.startAngle;  //以弧度表示的起始角度
            var e = items[i].__data__.endAngle;
            var direction = (e + s) / 2; //偏移方向对应的弧度
            var g = d3.select(items[i]);
            var clr = g.select("path").attr("fill");

            var offsetX = Math.sin(direction) * 10;
            var offsetY = Math.cos(direction) * 10;
            var d = arc.innerRadius(radius[0] - 20).outerRadius(radius[0] - 17).startAngle(s).endAngle(e)(items[i].__data__.data);
            var nd = arc.innerRadius(radius[0] - 10).outerRadius(radius[1])(items[i].__data__.data);
            if ($.isNumeric(hotPieIndex) & hotPieIndex !== i) {
                instance.Unhot();
            }
            g.select("path").attr("fill", clr).attr("d", nd);
            g.append("path").attr("fill", clr).attr("d", d).attr("class", "hotPath");
            hotPieIndex = i;
            transX += offsetX;
            transY -= offsetY;
            g.transition().ease("ease").duration(500).attr("transform", new Transform().Translate(transX, transY).toString());
        }

        return instance;
    };

    this.Unhot = function () {
        if ($.isNumeric(hotPieIndex) && hotPieIndex < items.length) {
            var transX = radius[1] + option.Padding;
            var transY = radius[1] + option.Padding;
            var s = items[hotPieIndex].__data__.startAngle;  //以弧度表示的起始角度
            var e = items[hotPieIndex].__data__.endAngle;
            var g = d3.select(items[hotPieIndex]);
            var d = arc.startAngle(s).endAngle(e).innerRadius(radius[0]).outerRadius(radius[1])(items[hotPieIndex].__data__.data);
            $(g[0]).find(".hotPath").remove();
            hotPieIndex = null;

            g.transition().ease("ease").duration(500).attr("transform", new Transform().Translate(transX, transY).toString());
            g.select("path").attr("d", d)
        }
        return instance;
    };

    this.Title = function (_txtElm) {
        /// <summary>
        /// 在环的约定位置显示相关文本
        /// </summary>
        /// <param name="_txtElm">标准DOM或JQuery Object</param>
        /// <returns type=""></returns>
        if (!_txtElm) {
            return titleWraper;
        }
        if (!dwr) {
            return instance;
        }
        if (!titleWraper) {
            titleWraper = $("<div>").css("position", "relative").css("display", "inline-block")
                                    .css("maxWidth", radius[0] * 2);
            $(dwr[0]).parent().append(titleWraper);
        }
        titleWraper.html("");
        titleWraper.append(_txtElm);
        titleWraper.css("top", 0 - (radius[1] + option.Padding + titleWraper.height() / 2) + "px")
                   .css("left", ((radius[1] + option.Padding) - titleWraper.width() / 2) + "px");
        return instance;
    };

    this.on = function (evType, fn) {
        $(items).on(evType, fn);
        return instance;
    };

    this.off = function (evType, fn) {
        $(items).off(evType, null);
        return instance;
    };

    this.Dispose = function () {
        dwr = null;
        arc = null;
        items = [];
        return this;
    };

    return instance;
};
CircleChartOption = {
    Padding: 20,  //环图形相对绘制区域的缩进量
    CircleWidth: 40,
    Legend: false,
    StyleForLegend: "CircleLegend"
};